import re
import getpass
import shlex
import subprocess as sp

from PySide2.QtCore import QRect, QSize, Qt, QRegExp
from PySide2.QtGui import QFont, QIntValidator, QDoubleValidator, QRegExpValidator
from PySide2.QtWidgets import *
# from CustWidgets.camera_tuner import CustomComboBox

btn_style_sheet = \
"""
QPushButton {
    background-color: rgb(0, 125, 196);
    color: rgb(255, 255, 255);
    border: 2px groove gray;
    border-radius: 2px;
    padding:2px 4px;
}
QPushButton:hover {
    background-color: rgb(170, 110, 0);
}
"""

large_int_dict = {
    "connection-speed": 18446744073709551,
    "max-rtcp-rtp-time-diff": 2147483647,
    "max-ts-offset": 9223372036854775807,
    "max-ts-offset-adjustment": 18446744073709551615,
    "probation": 4294967295,
    "tcp-timeout": 18446744073709551615,
    "teardown-timeout": 18446744073709551615,
    "timeout": 18446744073709551615,
    "udp-buffer-size": 2147483647,
    "latency": 4294967295

}

UNFOLD_CHAR = "\u25BC"
FOLD_CHAR = "\u25B2"


class CustomComboBox(QComboBox):

    def __init__(self, scroll_wgt=None, *args, **kwargs):
        super(CustomComboBox, self).__init__(*args, **kwargs)
        self.scroll_wgt = scroll_wgt
        self.setFocusPolicy(Qt.StrongFocus)

    def wheelEvent(self, event):
        if self.hasFocus():
            return QComboBox.wheelEvent(self, event)
        else:
            return event.ignore()


class RtspCameraTuner(QDialog):

    def __init__(self, parent=None, output_wgt=None):
        super(RtspCameraTuner, self).__init__(parent=parent)
        self.output_wgt = output_wgt
        self.init_ui()

    def init_ui(self):
        self.setWindowTitle("Rtsp Camera Tuner")
        self.resize(560, 580)
        self.grid_lyt_global = QGridLayout(self)

        self.all_categories_label = []
        self.all_parameters_label = []
        self.all_parameters_wgt = []

        self.init_top()
        self.init_central()
        self.init_bottom()
        self.set_categories_label_style()
        self.set_parameters_label_style()
        self.set_labels_tooltip()

        self.slots_collector()

    def init_top(self):
        self.lbl_camera_params = QLabel(self)
        self.lbl_camera_params.setText("Camera Parameters")
        font = QFont()
        font.setPointSize(16)
        self.lbl_camera_params.setFont(font)
        self.lbl_camera_params.setAlignment(Qt.AlignCenter)
        self.grid_lyt_global.addWidget(self.lbl_camera_params, 0, 0, 1, 11)

    def init_central(self):
        self.scroll_area = QScrollArea(self)
        self.scroll_area.setWidgetResizable(True)
        # 将滚动功能窗口添加进全局layout
        self.grid_lyt_global.addWidget(self.scroll_area, 1, 0, 1, 11)
        # 新建一个窗口：scroll_area_wgt
        self.scroll_area_wgt = QWidget()
        self.scroll_area_wgt.setGeometry(QRect(0, 0, 566, 564))

        # 在新建的窗口：scroll_area_wgt里面新建一个layout
        self.grid_lyt_scroll_area = QGridLayout(self.scroll_area_wgt)
        # 将新建的窗口放入滚动功能窗口
        self.scroll_area.setWidget(self.scroll_area_wgt)

        # ---------------------------- Basic ---------------------------- #
        # 在scroll_area_wgt窗口里面添加按钮：btn_basic
        self.btn_basic = QPushButton(self.scroll_area_wgt)
        self.btn_basic.setText("Basic")
        self.btn_basic.setStyleSheet("border: none")
        font = QFont()
        font.setPointSize(14)
        self.btn_basic.setFont(font)
        self.btn_basic.setDefault(False)
        self.btn_basic.setAutoDefault(False)
        # 在scroll_area_wgt窗口的layout中添加控件：btn_basic，放在第0行
        self.grid_lyt_scroll_area.addWidget(self.btn_basic, 0, 0, 1, 1)

        # 在scroll_area_wgt窗口里面添加窗口：wgt_basic
        self.wgt_basic = QWidget(self.scroll_area_wgt)
        self.grid_lyt_wgt_basic = QGridLayout(self.wgt_basic)
        # 在scroll_area_wgt窗口的layout中添加控件：wgt_basic，放在第1行
        self.grid_lyt_scroll_area.addWidget(self.wgt_basic, 1, 0, 1, 1)

        self.init_basic_location(index=0)
        self.init_basic_roi(index=1)

        # ---------------------------- Advanced ---------------------------- #
        self.btn_advanced = QPushButton(self.scroll_area_wgt)
        self.btn_advanced.setText(UNFOLD_CHAR + "  Advanced")
        self.btn_advanced.setStyleSheet("border: none")
        font = QFont()
        font.setPointSize(14)
        self.btn_advanced.setFont(font)
        self.btn_advanced.setDefault(False)
        self.btn_advanced.setAutoDefault(False)
        self.grid_lyt_scroll_area.addWidget(self.btn_advanced, 2, 0, 1, 1)

        self.wgt_advanced = QWidget(self.scroll_area_wgt)
        self.wgt_advanced.hide()
        self.grid_lyt_wgt_advanced = QGridLayout(self.wgt_advanced)
        self.grid_lyt_scroll_area.addWidget(self.wgt_advanced, 3, 0, 1, 1)

        self.init_advanced_params(index=0)

    def init_bottom(self):
        self.btn_vlc = QPushButton(self)
        self.btn_vlc.setText("Vlc")
        self.btn_vlc.setDefault(False)
        self.btn_vlc.setAutoDefault(False)
        self.btn_vlc.setStyleSheet(btn_style_sheet)
        self.btn_vlc.setMinimumSize(QSize(80, 0))
        self.btn_vlc.setMaximumSize(QSize(80, 16777215))
        self.grid_lyt_global.addWidget(self.btn_vlc, 2, 1, 1, 1)

        self.btn_tune = QPushButton(self)
        self.btn_tune.setText("Tune")
        self.btn_tune.setDefault(False)
        self.btn_tune.setAutoDefault(False)
        self.btn_tune.setStyleSheet(btn_style_sheet)
        self.btn_tune.setMinimumSize(QSize(80, 0))
        self.btn_tune.setMaximumSize(QSize(80, 16777215))
        self.grid_lyt_global.addWidget(self.btn_tune, 2, 3, 1, 1)

        self.btn_apply = QPushButton(self)
        self.btn_apply.setText("Apply")
        self.btn_apply.setDefault(False)
        self.btn_apply.setAutoDefault(False)
        self.btn_apply.setStyleSheet(btn_style_sheet)
        self.btn_apply.setMinimumSize(QSize(80, 0))
        self.btn_apply.setMaximumSize(QSize(80, 16777215))
        self.grid_lyt_global.addWidget(self.btn_apply, 2, 5, 1, 1)

        spacer1 = QSpacerItem(140, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.grid_lyt_global.addItem(spacer1, 2, 0, 1, 1)
        spacer2 = QSpacerItem(130, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.grid_lyt_global.addItem(spacer2, 2, 2, 1, 1)
        spacer3 = QSpacerItem(130, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.grid_lyt_global.addItem(spacer3, 2, 4, 1, 1)
        spacer3 = QSpacerItem(110, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.grid_lyt_global.addItem(spacer3, 2, 6, 1, 1)

    def set_categories_label_style(self):
        font = QFont()
        font.setPointSize(12)
        font.setItalic(True)
        font.setUnderline(True)

        for category_label in self.all_categories_label:
            category_label.setFont(font)
            category_label.setAlignment(Qt.AlignLeading | Qt.AlignLeft | Qt.AlignVCenter)

    def set_parameters_label_style(self):
        for param_label in self.all_parameters_label:
            param_label.setMinimumSize(QSize(180, 0))
            param_label.setMaximumSize(QSize(180, 16777215))

    def set_labels_tooltip(self):
        # Device
        self.lbl_ip.setToolTip(
            "Device's ip,Please make sure it is in the same network segment as "
            "\none of the network ports of your machine.")
        self.lbl_port.setToolTip("Device's port")

    def slots_collector(self):
        self.btn_advanced.clicked.connect(self.slot_btn_advanced_clicked)
        self.btn_vlc.clicked.connect(self._slot_btn_vlc_clicked)
        self.btn_tune.clicked.connect(self.slot_btn_tune_clicked)
        self.btn_apply.clicked.connect(self.slot_btn_apply_clicked)

    def slot_btn_advanced_clicked(self):
        if self.btn_advanced.text().startswith(UNFOLD_CHAR):
            self.wgt_advanced.show()
            self.btn_advanced.setText(FOLD_CHAR + "  Advanced")
        elif self.btn_advanced.text().startswith(FOLD_CHAR):
            self.wgt_advanced.hide()
            self.btn_advanced.setText(UNFOLD_CHAR + "  Advanced")

    def _slot_btn_vlc_clicked(self):
        abst_value, tmp_cmd, other_dict = self._collect_params()
        rtsp_url = self._generate_rtspurl(abst_value, tmp_cmd, other_dict)
        vlc_cmd = "vlc " + rtsp_url
        # print(vlc_cmd)
        cmd_list = shlex.split(vlc_cmd)
        proc = sp.Popen(cmd_list, shell=False, stdout=sp.PIPE, stderr=sp.STDOUT)
    def slot_btn_tune_clicked(self):
        gst_launch_cmd = self._generate_gst_cmd("tune")
        # print(gst_launch_cmd)
        cmd_list = shlex.split(gst_launch_cmd)
        proc = sp.Popen(cmd_list, shell=False, stdout=sp.PIPE, stderr=sp.STDOUT)

    def slot_btn_apply_clicked(self):
        if not self.output_wgt:
            QMessageBox.warning(self, "Warning",
                                "No widget to output.", QMessageBox.Ok)
            return

        if not hasattr(self.output_wgt, "setText"):
            QMessageBox.warning(self, "Warning",
                                "The widget cannot write text.", QMessageBox.Ok)
            return

        gst_launch_cmd = self._generate_gst_cmd("apply")

        # print(gst_launch_cmd)
        self.output_wgt.setText(gst_launch_cmd.strip())
        if hasattr(self.output_wgt, "setCursorPosition"):
            self.output_wgt.setCursorPosition(0)
        QMessageBox.information(self, "Info",
                                "Applied camera pipeline successfully!", QMessageBox.Ok)

    def _generate_gst_cmd(self, button):
        abst_value, tmp_cmd, other_dict = self._collect_params()
        rtsp_url = self._generate_rtspurl(abst_value, tmp_cmd, other_dict)
        rtsp_url += " "
        # rtspsrc location=\"rtsp://192.168.1.10:554/user=admin&password=&channel=1&stream=0.sdp?\" latency=100  !
        # rtph264depay ! h264parse ! vaapih264dec ! vaapipostproc format=bgrx height=600 width=600 ! videoconvert !
        # video/x-raw,format=BGR ! appsink"
        for key in list(other_dict.keys()):
            if not other_dict.get(key):
                del other_dict[key]
        if other_dict.__contains__("height"):
            re_size = ",height={},width={}".format(other_dict["height"], other_dict["width"]) if \
                other_dict.__contains__("width") else ",height={}".format(other_dict["height"])
        else:
            re_size = ",width={}".format(other_dict["width"]) if other_dict.__contains__("width") else ""
        if other_dict["postproc"] == "(default)":
            gst_launch_cmd = "rtspsrc location=" + rtsp_url + \
                             "! {} ! {} ! {} ! " \
                             "videoconvert ! videoscale ! video/x-raw,format=BGR{} ! "
            gst_launch_cmd = gst_launch_cmd.format(other_dict["rtph_depay"], other_dict["parse"],
                                                   other_dict["decode"], re_size)
        else:
            if re_size != "":
                re_size = re_size.replace(",", " ")
            else:
                re_size = ' width=600 height=600'
            gst_launch_cmd = "rtspsrc location=" + rtsp_url + \
                             "! {} ! {} ! {} ! " \
                             "{} format=bgrx{} ! " \
                             "videoconvert ! videoscale ! video/x-raw,format=BGR ! "
            gst_launch_cmd = gst_launch_cmd.format(other_dict["rtph_depay"], other_dict["parse"],
                                                   other_dict["decode"], other_dict["postproc"], re_size)

        if button == "tune":
            gst_launch_cmd = gst_launch_cmd.replace(",format=BGR", "")
            if other_dict["postproc"] != "(default)":
                gst_launch_cmd = gst_launch_cmd.replace(" format=bgrx", "")
            gst_launch_cmd += "ximagesink"
            gst_launch_cmd = "gst-launch-1.0 " + gst_launch_cmd
        elif button == "apply":
            gst_launch_cmd += "appsink"
        else:
            pass
        return gst_launch_cmd

    def _generate_rtspurl(self, abst_value, tmp_cmd, other_dict):
        m_str = ""
        rtsp_url = ""
        if other_dict["brand"] == "XiongMai":
            rtsp_url = '"rtsp://{}:{}/{}.sdp?"{}'
            rtsp_url = rtsp_url.format(abst_value.pop("ip"), abst_value.pop("port"), "{}", "{}")
            for k, v in abst_value.items():
                m_str += k + "=" + v + "&"
            m_str = m_str[:-1]
            rtsp_url = rtsp_url.format(m_str, tmp_cmd)
        elif other_dict["brand"] == "DaHua":
            # rtsp://admin:admin@10.12.4.84:554/cam/realmonitor?channel=2&subtype=1
            rtsp_url = '"rtsp://{}:{}@{}:{}/cam/realmonitor?channel={}&subtype={}"{}'
            rtsp_url = rtsp_url.format(abst_value["user"], abst_value["password"],
                                     abst_value["ip"], abst_value["port"],
                                     abst_value["channel"], abst_value["stream"],
                                     tmp_cmd)
        elif other_dict["brand"] == "HikVision":
            # rtsp://admin:12345@192.0.0.64:554/h264/ch1/main/av_stream
            rtsp_url = '"rtsp://{}:{}@{}:{}/h264/ch{}/{}/av_stream"{}'
            rtsp_url = rtsp_url.format(abst_value["user"], abst_value["password"],
                                     abst_value["ip"], abst_value["port"],
                                     abst_value["channel"], "sub" if int(abst_value["stream"]) else "main",
                                     tmp_cmd)
        return rtsp_url

    def _collect_params(self):
        if self.le_ip.text() == "":
            QMessageBox.warning(self, "Warning",
                "The paramter, \"ip\" should not be empty.", QMessageBox.Ok)
            return ""
        if self.le_port.text() == "":
            QMessageBox.warning(self, "Warning",
                "The paramter, \"port\" should not be empty.", QMessageBox.Ok)
            return ""
        if self.le_user.text() == "":
            QMessageBox.warning(self, "Warning",
                "The paramter, \"user\" should not be empty.", QMessageBox.Ok)
            return ""

        self.filtered_parameters_label = self.all_parameters_label.copy()
        self.filtered_parameters_wgt = self.all_parameters_wgt.copy()
        # Prepare the params to rtsp url
        tmp_cmd = ""
        other_dict = {"brand": "", "width": "", "height": "",
                      "rtph_depay": "", "parse": "", "decode": "",
                      "postproc": ""}
        for param_label, param_wgt in zip(self.all_parameters_label, self.all_parameters_wgt):
            lbl = param_label.text()
            if lbl in other_dict.keys():
                try:
                    other_dict[lbl] = param_wgt.text()
                except:
                    other_dict[lbl] = param_wgt.currentText()
                self.filtered_parameters_label.remove(param_label)
                self.filtered_parameters_wgt.remove(param_wgt)
        abst_value = {"ip": "", "port": "", "user": "",
                      "password": "", "channel": "", "stream": ""}
        enditer_error = False
        for param_label, param_wgt in zip(self.filtered_parameters_label, self.filtered_parameters_wgt):
            lbl = param_label.text()
            try:
                v_wgt = param_wgt.text()
            except:
                v_wgt = param_wgt.currentText()
            if "*" in lbl:
                lbl = lbl.split("<")[0]
            if lbl in large_int_dict and v_wgt != "":
                v_wgt = int(v_wgt)
                if v_wgt > large_int_dict[lbl]:
                    QMessageBox.warning(self, "Warning",
                                        "The paramter, \"{}\" "
                                        "should not be greater than {}.".format(lbl, large_int_dict[lbl]),
                                        QMessageBox.Ok)
                    enditer_error = True
                    return
            if lbl in abst_value:
                    abst_value[lbl] = v_wgt
            elif isinstance(param_wgt, QLineEdit) and param_wgt.text() != "":
                tmp_cmd += " {}={}".format(
                    lbl, v_wgt
                )
            elif isinstance(param_wgt, CustomComboBox) and param_wgt.currentText() != "(default)":
                tmp_cmd += " {}={}".format(
                    lbl, v_wgt
                )
        if enditer_error:
            return ""
        return abst_value, tmp_cmd, other_dict

    def init_basic_location(self, index):
        # ---------------------------- Basic: Device ---------------------------- #
        # Location label
        self.title_location = QLabel(self.wgt_basic)
        self.title_location.setText("Location")
        self.grid_lyt_wgt_basic.addWidget(self.title_location, 2 * index, 0, 1, 1)
        self.all_categories_label.append(self.title_location)

        # Location widget
        self.wgt_location = QWidget(self.wgt_basic)
        form_lyt_device = QFormLayout(self.wgt_location)
        self.grid_lyt_wgt_basic.addWidget(self.wgt_location, 2 * index + 1, 0, 1, 1)

        # Location widget: ip
        self.lbl_ip = QLabel(self.wgt_location)
        self.lbl_ip.setText("ip<font color=red>*</font>")
        form_lyt_device.setWidget(1, QFormLayout.LabelRole, self.lbl_ip)
        self.le_ip = QLineEdit(self.wgt_location)
        self.le_ip.setPlaceholderText("Type: string")
        # Set IP Validator
        regexp = QRegExp(
            '^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){0,3}$')
        validator = QRegExpValidator(regexp)
        self.le_ip.setValidator(validator)
        form_lyt_device.setWidget(1, QFormLayout.FieldRole, self.le_ip)
        self.all_parameters_label.append(self.lbl_ip)
        self.all_parameters_wgt.append(self.le_ip)

        # Location widget: port
        self.lbl_port = QLabel(self.wgt_location)
        self.lbl_port.setText("port<font color=red>*</font>")
        form_lyt_device.setWidget(2, QFormLayout.LabelRole, self.lbl_port)
        self.le_port = QLineEdit(self.wgt_location)
        self.le_port.setText("554")
        self.le_port.setPlaceholderText("Type: integer;  Range: 0 - 65535")
        self.le_port.setValidator(QIntValidator(0, 65535))
        form_lyt_device.setWidget(2, QFormLayout.FieldRole, self.le_port)
        self.all_parameters_label.append(self.lbl_port)
        self.all_parameters_wgt.append(self.le_port)

        # Location widget: user
        self.lbl_user = QLabel(self.wgt_location)
        self.lbl_user.setText("user<font color=red>*</font>")
        form_lyt_device.setWidget(3, QFormLayout.LabelRole, self.lbl_user)
        self.le_user = QLineEdit(self.wgt_location)
        self.le_user.setPlaceholderText("Type: string")
        form_lyt_device.setWidget(3, QFormLayout.FieldRole, self.le_user)
        self.all_parameters_label.append(self.lbl_user)
        self.all_parameters_wgt.append(self.le_user)

        # Location widget: password
        self.lbl_psd = QLabel(self.wgt_location)
        self.lbl_psd.setText("password")
        form_lyt_device.setWidget(4, QFormLayout.LabelRole, self.lbl_psd)
        self.le_psd = QLineEdit(self.wgt_location)
        self.le_psd.setPlaceholderText("Type: string")
        form_lyt_device.setWidget(4, QFormLayout.FieldRole, self.le_psd)
        self.all_parameters_label.append(self.lbl_psd)
        self.all_parameters_wgt.append(self.le_psd)

        # Location widget: camera brand
        self.lbl_brand = QLabel(self.wgt_location)
        self.lbl_brand.setText("brand")
        form_lyt_device.setWidget(5, QFormLayout.LabelRole, self.lbl_brand)
        self.cbb_brand = CustomComboBox(QFrame(QScrollArea()))
        self.cbb_brand.addItems([
            "XiongMai", "HikVision", "DaHua"
        ])
        form_lyt_device.setWidget(5, QFormLayout.FieldRole, self.cbb_brand)
        self.all_parameters_label.append(self.lbl_brand)
        self.all_parameters_wgt.append(self.cbb_brand)

        # Location widget: channel
        self.lbl_chl = QLabel(self.wgt_location)
        self.lbl_chl.setText("channel")
        form_lyt_device.setWidget(6, QFormLayout.LabelRole, self.lbl_chl)
        self.le_chl = QLineEdit(self.wgt_location)
        self.le_chl.setText("1")
        self.le_chl.setValidator(QIntValidator(1, 65535))
        self.le_chl.setPlaceholderText("Type: integer;  Range: 0 - 65535")
        form_lyt_device.setWidget(6, QFormLayout.FieldRole, self.le_chl)
        self.all_parameters_label.append(self.lbl_chl)
        self.all_parameters_wgt.append(self.le_chl)

        # Location widget: stream
        self.lbl_stream = QLabel(self.wgt_location)
        self.lbl_stream.setText("stream")
        form_lyt_device.setWidget(7, QFormLayout.LabelRole, self.lbl_stream)
        self.cbb_stream = CustomComboBox(QFrame(QScrollArea()))
        self.cbb_stream.addItems([
            "0", "1",
        ])
        form_lyt_device.setWidget(7, QFormLayout.FieldRole, self.cbb_stream)
        self.all_parameters_label.append(self.lbl_stream)
        self.all_parameters_wgt.append(self.cbb_stream)


    def init_basic_roi(self, index):
        # ---------------------------- Basic: ROI ---------------------------- #
        # ROI label
        self.title_roi = QLabel(self.wgt_basic)
        self.title_roi.setText("ROI")
        self.grid_lyt_wgt_basic.addWidget(self.title_roi, 2 * index, 0, 1, 1)
        self.all_categories_label.append(self.title_roi)

        # Device ROI
        self.wgt_roi = QWidget(self.wgt_basic)
        form_lyt_roi = QFormLayout(self.wgt_roi)
        self.grid_lyt_wgt_basic.addWidget(self.wgt_roi, 2 * index + 1, 0, 1, 1)

        # ROI rtph_depay
        self.lbl_rtph = QLabel(self.wgt_roi)
        self.lbl_rtph.setText("rtph_depay")
        form_lyt_roi.setWidget(1, QFormLayout.LabelRole, self.lbl_rtph)
        self.cbb_rtph = CustomComboBox(QFrame(QScrollArea()))
        self.cbb_rtph.addItems([
            "rtph264depay", "rtph265depay", "rtph263depay", "rtph261depay"
        ])
        form_lyt_roi.setWidget(1, QFormLayout.FieldRole, self.cbb_rtph)
        self.all_parameters_label.append(self.lbl_rtph)
        self.all_parameters_wgt.append(self.cbb_rtph)

        # ROI parse
        self.lbl_parse = QLabel(self.wgt_roi)
        self.lbl_parse.setText("parse")
        form_lyt_roi.setWidget(2, QFormLayout.LabelRole, self.lbl_parse)
        self.cbb_parse = CustomComboBox(QFrame(QScrollArea()))
        self.cbb_parse.addItems([
            "h264parse", "h265parse", "h263parse"
        ])
        form_lyt_roi.setWidget(2, QFormLayout.FieldRole, self.cbb_parse)
        self.all_parameters_label.append(self.lbl_parse)
        self.all_parameters_wgt.append(self.cbb_parse)

        # ROI decode
        self.lbl_decode = QLabel(self.wgt_roi)
        self.lbl_decode.setText("decode")
        form_lyt_roi.setWidget(3, QFormLayout.LabelRole, self.lbl_decode)
        self.cbb_decode = CustomComboBox(QFrame(QScrollArea()))
        self.cbb_decode.addItems([
            "avdec_h264", "avdec_h265", "vaapih264dec", "vaapih265dec"
        ])
        form_lyt_roi.setWidget(3, QFormLayout.FieldRole, self.cbb_decode)
        self.all_parameters_label.append(self.lbl_decode)
        self.all_parameters_wgt.append(self.cbb_decode)

        # ROI postproc
        self.lbl_postproc = QLabel(self.wgt_roi)
        self.lbl_postproc.setText("postproc")
        form_lyt_roi.setWidget(4, QFormLayout.LabelRole, self.lbl_postproc)
        self.cbb_postproc = CustomComboBox(QFrame(QScrollArea()))
        self.cbb_postproc.addItems([
            "(default)", "vaapipostproc"
        ])
        form_lyt_roi.setWidget(4, QFormLayout.FieldRole, self.cbb_postproc)
        self.all_parameters_label.append(self.lbl_postproc)
        self.all_parameters_wgt.append(self.cbb_postproc)

        # ROI width
        self.lbl_width = QLabel(self.wgt_roi)
        self.lbl_width.setText("width")
        form_lyt_roi.setWidget(5, QFormLayout.LabelRole, self.lbl_width)
        self.le_width = QLineEdit(self.wgt_roi)
        self.le_width.setValidator(QIntValidator(0, 2147483647))
        self.le_width.setPlaceholderText("Type: integer;  Range: 0 - 2147483647")
        form_lyt_roi.setWidget(5, QFormLayout.FieldRole, self.le_width)
        self.all_parameters_label.append(self.lbl_width)
        self.all_parameters_wgt.append(self.le_width)

        # ROI height
        self.lbl_height = QLabel(self.wgt_roi)
        self.lbl_height.setText("height")
        form_lyt_roi.setWidget(6, QFormLayout.LabelRole, self.lbl_height)
        self.le_height = QLineEdit(self.wgt_roi)
        self.le_height.setValidator(QIntValidator(0, 2147483647))
        self.le_height.setPlaceholderText("Type: integer;  Range: 0 - 2147483647")
        form_lyt_roi.setWidget(6, QFormLayout.FieldRole, self.le_height)
        self.all_parameters_label.append(self.lbl_height)
        self.all_parameters_wgt.append(self.le_height)

    def init_advanced_params(self, index):
        # ---------------------------- Advanced: params ---------------------------- #
        # rtspsrc label
        self.title_rtspsrc = QLabel(self.wgt_advanced)
        self.title_rtspsrc.setText("RTSPSRC")
        self.grid_lyt_wgt_advanced.addWidget(self.title_rtspsrc, 2 * index, 0, 1, 1)
        self.all_categories_label.append(self.title_rtspsrc)

        # rtspsrc widget
        self.wgt_rtspsrc = QWidget(self.wgt_advanced)
        form_lyt_rtspsrc_control = QFormLayout(self.wgt_rtspsrc)
        self.grid_lyt_wgt_advanced.addWidget(self.wgt_rtspsrc, 2 * index + 1, 0, 1, 1)

        text_str = "Type: string"
        text_int = "Type: integer"
        tf_list = ["false", "true"]
        pro_list = ["0x00000000", "0x00000001", "0x00000002", "0x00000003",
                    "0x00000004", "0x00000005", "0x00000006", "0x00000007",
                    "0x00000010", "0x00000011", "0x00000012", "0x00000013",
                    "0x00000014", "0x00000015", "0x00000016", "0x00000017",
                    "0x00000020", "0x00000021", "0x00000022", "0x00000023",
                    "0x00000024", "0x00000025", "0x00000026", "0x00000027",
                    "0x00000030", "0x00000031", "0x00000032", "0x00000033",
                    "0x00000034", "0x00000035", "0x00000036", "0x00000037"]
        tlsflat_list = ["0x00000001", "0x00000002", "0x00000004", "0x00000008",
                        "0x00000010", "0x00000020", "0x00000040", "0x0000007f"]

        rtspsrc_label_list = [["name", text_str], ["latency", text_int],
                             ["drop-on-latency", tf_list], ["timeout", text_int],
                             ["nat-method", ["0" if i == "false" else "1" for i in tf_list]],
                             ["protocols", pro_list], ["proxy", text_str],
                             ["proxy-id", text_str], ["proxy-pw", text_str],
                             ["retry", text_int], ["tcp-timeout", text_int],
                             ["async-handling", tf_list],
                             ["backchannel", ["0", "1"]],
                             ["buffer-mode", ["0", "1", "2", "3", "4"]],
                             ["connection-speed", text_int], ["debug", tf_list],
                             ["default-rtsp-version", ["16", "0", "17", "32"]],
                             ["do-retransmission", tf_list], ["do-rtcp", tf_list],
                             ["do-rtsp-keep-alive", tf_list], ["max-rtcp-rtp-time-diff", text_int],
                             ["ntp-sync", tf_list], ["max-ts-offset", text_int],
                             ["max-ts-offset-adjustment", text_int],
                             ["message-forward", tf_list], ["multicast-iface", text_str],
                             ["ntp-time-source", ["0", "1", "2", "3"]],
                             ["parent", text_str], ["port-range", text_str],
                             ["probation", text_int], ["rfc7273-sync", tf_list],
                             ["rtp-blocksize", text_int], ["sdes", text_str],
                             ["short-header", tf_list], ["teardown-timeout", text_int],
                             ["tls-database", text_str], ["tls-interaction", text_str],
                             ["tls-validation-flags", tlsflat_list],
                             ["udp-buffer-size", text_int], ["udp-reconnect", tf_list],
                             ["use-pipeline-clock", tf_list], ["user-agent", text_str],
                             ["user-id", text_str], ["user-pw", text_str]]
        i = 1
        for row_info in rtspsrc_label_list:
            if isinstance(row_info[-1], str):
                self.label_and_wgt(self.wgt_rtspsrc, form_lyt_rtspsrc_control, row_info[0], row_info[1], i)
            elif isinstance(row_info[-1], list):
                self.label_and_combox(self.wgt_rtspsrc, form_lyt_rtspsrc_control, row_info[0], row_info[1].copy(), i)
            i += 1


    def label_and_wgt(self, wgt_other, form_lyt_control, label_name, text_type, row_num, rex="[0-9]{20}"):
        # rtspsrc widget: name
        lbl_other = QLabel(wgt_other)
        lbl_other.setText(label_name)
        form_lyt_control.setWidget(row_num, QFormLayout.LabelRole, lbl_other)
        le_other = QLineEdit(wgt_other)
        le_other.setPlaceholderText(text_type)
        if "integer" in text_type:
            # Set Validator
            regexp = QRegExp(rex)
            validator = QRegExpValidator(regexp)
            le_other.setValidator(validator)
        form_lyt_control.setWidget(row_num, QFormLayout.FieldRole, le_other)
        self.all_parameters_label.append(lbl_other)
        self.all_parameters_wgt.append(le_other)

    def label_and_combox(self, wgt_other, form_lyt_control, label_name, content_list, row_num):
        lbl_other = QLabel(wgt_other)
        lbl_other.setText(label_name)
        form_lyt_control.setWidget(row_num, QFormLayout.LabelRole, lbl_other)
        cbb_other = CustomComboBox(QFrame(QScrollArea()))
        content_list.insert(0, "(default)")
        cbb_other.addItems(content_list)
        form_lyt_control.setWidget(row_num, QFormLayout.FieldRole, cbb_other)
        self.all_parameters_label.append(lbl_other)
        self.all_parameters_wgt.append(cbb_other)


if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    win = RtspCameraTuner()
    win.show()
    sys.exit(app.exec_())